﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using Eniux.Windows.ThreeDEngine.Helper;
using System.Windows.Media.Media3D;
using System.Windows.Media;
using System.Windows;
using System.Globalization;
using System.Windows.Media.Animation;
using Eniux.Windows.ThreeDEngine.AttachObject;

namespace Eniux.Windows.ThreeDEngine.Visual3Ds
{
    public class RubikCube : ModelVisual3D
    {

        #region 私有变量
        Brush faceFill=Brushes.Silver;
        Color faceFillColor = Colors.Silver;
        Brush faceFillByMouseOver=Brushes.Gray;
        Color faceFillColorByMouseOver = Colors.Gray;
        Color FaceColor = Colors.Orange;
        // The indices of the faces are 
        // 0:Back 
        // 1:Front 
        // 2:Left 
        // 3:Right 
        // 4:Down 
        // 5:Up

        private double spacing = 0;
        private double size = 5.7;

        /// <summary>
        /// This array keeps track of which cubelet is located in each
        /// position (i,j,k) in the cube.
        /// </summary>
        private Model3DGroup[, ,] cubelets;
        #endregion

        #region 构造
        /// <summary>
        /// 构造、析构
        /// </summary>
        public RubikCube()
        {
            AttachViewCube.AddOwner(typeof(RubikCube));
            CreateCubelets();
        } 
        #endregion

        #region 注册属性

        #region Size

        /// <summary>
        /// Size属性
        /// </summary>
        public static readonly DependencyProperty SizeProperty =
        DependencyProperty.Register("Size", typeof(int), typeof(RubikCube), new UIPropertyMetadata(3, SizeChanged));

        /// <summary>
        /// 获取或设置Size
        /// </summary>
        public int Size
        {
            get { return (int)GetValue(SizeProperty); }
            set { SetValue(SizeProperty, value); }
        }

        private static void SizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((RubikCube)d).OnSizeChanged();
        }

        #endregion 

        #endregion

        #region  方法
        private void OnSizeChanged()
        {
            CreateCubelets();
        }

        private static Brush CreateFaceBrush(Color c, string text,Face face,Alignment align)
        {
            var db = new DrawingBrush
            {
                TileMode = TileMode.None,
                ViewportUnits = BrushMappingMode.Absolute,
                Viewport = new Rect(0, 0, 1, 1),
                Viewbox = new Rect(0, 0, 1, 1),
                ViewboxUnits = BrushMappingMode.Absolute
            };
            var dg = new DrawingGroup();
            dg.Children.Add(new GeometryDrawing { Geometry = new RectangleGeometry(new Rect(0, 0, 1, 1)) { }, Brush = Brushes.Sienna });
            dg.Children.Add(new GeometryDrawing
                                {
                                    Geometry = new RectangleGeometry(new Rect(0.01, 0.01, 0.99, 0.99)) { },
                                    Brush = new SolidColorBrush(c)
                                });

            if (text != null)
            {
                var ft = new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
                                           new Typeface("Segoe UI"), 0.8, Brushes.Black);
                ft.TextAlignment = TextAlignment.Justify;
                var geometry = ft.BuildGeometry(new Point(0, 0));
                if (align == Alignment.Center)
                {
                    var tg = new TransformGroup();
                    if (face == Face.Front || face == Face.Right)
                    {

                        tg.Children.Add(new RotateTransform(180) { CenterX = 0.5, CenterY = 0.5 });
                        // tg.Children.Add(new TranslateTransform(0, 0));
                    }
                    if (face == Face.Top||face==Face.Bottom)
                    {
                        tg.Children.Add(new RotateTransform(90) { CenterX = 0.5, CenterY = 0.5 });
                    }
                    geometry.Transform = tg;
                }
                dg.Children.Add(new GeometryDrawing
                {
                    Geometry = geometry,
                    Brush = Brushes.Black

                });
            }
            db.Drawing = dg;
            return db;
        }

        private void CreateCubelets()
        {
            Children.Clear();
            cubelets = new Model3DGroup[Size, Size, Size];
            double o = -(Size - 1) * 0.5 * size;

            for (int i = 0; i < Size; i++)
            {
                for (int j = 0; j < Size; j++)
                {
                    for (int k = 0; k < Size; k++)
                    {
                        // the center of the cubelet
                        var center = new Point3D(o + i * size, o + j * size, o + k * size);

                        // add the 6 faces of a cubelet
                        //var cubelet = new Model3DGroup();
                        //ModelUIElement3D element = new ModelUIElement3D();
                        for (int face = 0; face < 6; face++)
                        {
                            ModelUIElement3D faceElement = new ModelUIElement3D();

                            // find the color of the face
                            faceFill = IsOutsideFace(face, i, j, k) ? CreateFaceBrush(faceFillColor, null,Face.None,Alignment.None) : Brushes.Black;
                            if (face == 0 && i == 0 && j == 1 && k == 1)
                            {
                                //AttachViewCube.SetText(element, AttachViewCube.GetBackText(this));
                                //AttachViewCube.SetAlignment(element, Alignment.Center);
                                //AttachViewCube.SetFace(element, Face.Back);

                                AttachViewCube.SetText(faceElement, AttachViewCube.GetBackText(this));
                                AttachViewCube.SetAlignment(faceElement, Alignment.Center);
                                AttachViewCube.SetFace(faceElement, Face.Back);
                                faceFill = CreateFaceBrush(faceFillColor, AttachViewCube.GetBackText(this), Face.Back, Alignment.Center);
                            }
                            if (face == 1 && i == 2 && j == 1 && k == 1)
                            {
                                //AttachViewCube.SetText(element, AttachViewCube.GetFrontText(this));
                                //AttachViewCube.SetAlignment(element, Alignment.Center);
                                //AttachViewCube.SetFace(element, Face.Front);

                                AttachViewCube.SetText(faceElement, AttachViewCube.GetFrontText(this));
                                AttachViewCube.SetAlignment(faceElement, Alignment.Center);
                                AttachViewCube.SetFace(faceElement, Face.Front);


                                faceFill = CreateFaceBrush(faceFillColor, AttachViewCube.GetFrontText(this), Face.Front, Alignment.Center);
                            }
                            if (face == 2 && i == 1 && j == 0 && k == 1)
                            {
                                //AttachViewCube.SetText(element, AttachViewCube.GetLeftText(this));
                                //AttachViewCube.SetAlignment(element, Alignment.Center);
                                //AttachViewCube.SetFace(element, Face.Left);

                                AttachViewCube.SetText(faceElement, AttachViewCube.GetLeftText(this));
                                AttachViewCube.SetAlignment(faceElement, Alignment.Center);
                                AttachViewCube.SetFace(faceElement, Face.Left);
                                faceFill = CreateFaceBrush(faceFillColor, AttachViewCube.GetLeftText(this), Face.Left, Alignment.Center);
                            }
                            if (face == 3 && i == 1 && j == 2 && k == 1)
                            {
                                //AttachViewCube.SetText(element, AttachViewCube.GetRightText(this));
                                //AttachViewCube.SetAlignment(element, Alignment.Center);
                                //AttachViewCube.SetFace(element, Face.Right);

                                AttachViewCube.SetText(faceElement, AttachViewCube.GetRightText(this));
                                AttachViewCube.SetAlignment(faceElement, Alignment.Center);
                                AttachViewCube.SetFace(faceElement, Face.Right);
                                faceFill = CreateFaceBrush(faceFillColor, AttachViewCube.GetRightText(this), Face.Right, Alignment.Center);
                            }
                            if (face == 4 && i == 1 && j == 1 && k == 0)
                            {
                                //AttachViewCube.SetText(element, AttachViewCube.GetBottomText(this));
                                //AttachViewCube.SetAlignment(element, Alignment.Center);
                                //AttachViewCube.SetFace(element, Face.Bottom);

                                AttachViewCube.SetText(faceElement, AttachViewCube.GetBottomText(this));
                                AttachViewCube.SetAlignment(faceElement, Alignment.Center);
                                AttachViewCube.SetFace(faceElement, Face.Bottom);
                                faceFill = CreateFaceBrush(faceFillColor, AttachViewCube.GetBottomText(this), Face.Bottom, Alignment.Center);
                            }
                            if (face == 5 && i == 1 && j == 1 && k == 2)
                            {
                                //AttachViewCube.SetText(element, AttachViewCube.GetTopText(this));
                                //AttachViewCube.SetAlignment(element, Alignment.Center);
                                //AttachViewCube.SetFace(element, Face.Top);

                                AttachViewCube.SetText(faceElement, AttachViewCube.GetTopText(this));
                                AttachViewCube.SetAlignment(faceElement, Alignment.Center);
                                AttachViewCube.SetFace(faceElement, Face.Top);
                                faceFill = CreateFaceBrush(faceFillColor, AttachViewCube.GetTopText(this), Face.Top, Alignment.Center);
                            }
                            // and add a cube face
                            if (faceFill != Brushes.Black)
                            {
                              //  faceElement = new ModelUIElement3D();
                                faceElement.Model = CreateFace(face, center, size * (1 - spacing), size * (1 - spacing) , size * (1 - spacing), faceFill);
                                //if (AttachViewCube.GetAlignment(faceElement) == Alignment.Center)
                                //{
                                //    center.X /= 2;
                                //    center.Y /= 2;
                                //    center.Z /= 2;
                                //    faceElement.Model = CreateFace(face, center, size * (1 - spacing) * 2, size * (1 - spacing) * 2, size * (1 - spacing) *2, faceFill);
                                //}
                                //if (AttachViewCube.GetAlignment(faceElement) != Alignment.Center)
                                //{
                                //    //center.X /= 2;
                                //    //center.Y /= 2;
                                //    //center.Z /= 2;
                                //    faceElement.Model = CreateFace(face, center, size * (1 - spacing) / 2, size * (1 - spacing) / 2, size * (1 - spacing) / 2, faceFill);
                                //}
                                faceElement.MouseEnter += (sender, e) =>
                                {
                                    ChangeFaceStatus(sender as ModelUIElement3D, true);
                                };
                                faceElement.MouseLeave += (sender, e) =>
                                {
                                    ChangeFaceStatus(sender as ModelUIElement3D, false);
                                };
                                Children.Add(faceElement);
                                //cubelet.Children.Add(CreateFace(face, center, size * (1 - spacing), size * (1 - spacing), size * (1 - spacing), faceFill));
                            }
                        }
                       // cubelets[i, j, k] = cubelet;
                        //element.Model = cubelet;
                        //element.MouseEnter += (sender, e) => 
                        //{
                        //    ChangeModelStatus(sender as ModelUIElement3D, true); 
                        //};
                        //element.MouseLeave += (sender, e) =>
                        //{
                        //    ChangeModelStatus(sender as ModelUIElement3D, false);
                        //};

                      //  Children.Add(element);
  
                    }
                }
            }
        }

        //private void ChangeModelStatus(ModelUIElement3D element,bool IsMouseOver)
        //{
        //    Model3DGroup model3d = element.Model as Model3DGroup;
        //    Color color;
        //    if (IsMouseOver)
        //    {
        //        color = faceFillColorByMouseOver;
        //        color.A = 130;
        //    }
        //    else
        //        color = faceFillColor;
        //    if (AttachViewCube.GetAlignment(element) == Alignment.Center)
        //    {
        //        if (model3d.Children.Count != 0)
        //        {
        //            (model3d.Children[0] as GeometryModel3D).Material =
        //                MaterialHelper.CreateMaterial(CreateFaceBrush(color,
        //                AttachViewCube.GetText(element),
        //                AttachViewCube.GetFace(element),
        //                AttachViewCube.GetAlignment(element)));
        //        }
        //    }
        //    else
        //    {
        //        if (model3d.Children.Count != 0)
        //        {

        //            foreach (GeometryModel3D item in model3d.Children)
        //            {
        //                item.Material = MaterialHelper.CreateMaterial(CreateFaceBrush(color,
        //                               AttachViewCube.GetText(element),
        //                               AttachViewCube.GetFace(element),
        //                               AttachViewCube.GetAlignment(element))); 
        //            }
        //        }
        //    }
        //}

        private void ChangeFaceStatus(ModelUIElement3D element, bool IsMouseOver)
        {
            GeometryModel3D geo = element.Model as GeometryModel3D;
            Color color;
            if (IsMouseOver)
                color = FaceColor;
            else
                color = faceFillColor;
            if (AttachViewCube.GetAlignment(element) == Alignment.Center)
            {
                geo.Material =
                    MaterialHelper.CreateMaterial(CreateFaceBrush(color,
                    AttachViewCube.GetText(element),
                    AttachViewCube.GetFace(element),
                    AttachViewCube.GetAlignment(element)));
            }
            else
            {
                geo.Material = MaterialHelper.CreateMaterial(CreateFaceBrush(color,
                                AttachViewCube.GetText(element),
                                AttachViewCube.GetFace(element),
                                AttachViewCube.GetAlignment(element)));
            }
        }

        /// <summary>
        /// Determines whether a face given a position in the cube is on the outside.
        /// If it is not, we will give the face black colour.
        /// </summary>
        private bool IsOutsideFace(int face, int i, int j, int k)
        {
            switch (face)
            {
                case 0:
                    return i == 0;
                case 1:
                    return i == Size - 1;
                case 2:
                    return j == 0;
                case 3:
                    return j == Size - 1;
                case 4:
                    return k == 0;
                case 5:
                    return k == Size - 1;
            }
            return false;
        }

        private static GeometryModel3D CreateFace(int face, Point3D center, double width, double length, double height, Brush brush)
        {
            var m = new GeometryModel3D();
            var b = new MeshBuilder();
            switch (face)
            {
                case 0:
                    b.AddCubeFace(center, new Vector3D(-1, 0, 0), new Vector3D(0, 0, 1), length, width, height);
                    break;
                case 1:
                    b.AddCubeFace(center, new Vector3D(1, 0, 0), new Vector3D(0, 0, -1), length, width, height);
                    break;
                case 2:
                    b.AddCubeFace(center, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), width, length, height);
                    break;
                case 3:
                    b.AddCubeFace(center, new Vector3D(0, 1, 0), new Vector3D(0, 0, -1), width, length, height);
                    break;
                case 4:
                    b.AddCubeFace(center, new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), height, length, width);
                    break;
                case 5:
                    b.AddCubeFace(center, new Vector3D(0, 0, 1), new Vector3D(0, -1, 0), height, length, width);
                    break;
            }

            m.Geometry = b.ToMesh();
            m.Material = MaterialHelper.CreateMaterial(brush);
            return m;
        } 
        #endregion

    }


}
